#include <iostream>
#include <cmath>

#include "visitor.h"

// #define TEST

void Visitor::setValue(antlr4::tree::ParseTree *node, llvm::Value *value){
    valueTree.put(node, value);
}

llvm::Value *Visitor::getValue(antlr4::tree::ParseTree *node){
    return valueTree.get(node);
}

antlrcpp::Any Visitor::visitStat(calculatorParser::StatContext *context) {
    #ifdef TEST
    std::cout << "start visit stat" << std::endl;
    #endif
    visit(context->exp());
    GlobalContext &gctx = GlobalContext::GetInstance();
    gctx.builder->CreateRet(getValue(context->exp()));
    #ifdef TEST
    std::cout << "end visit stat" << std::endl;
    #endif
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitExpUnary(calculatorParser::ExpUnaryContext *context) {
    #ifdef TEST
    std::cout << "start visit exp unary" << std::endl;
    #endif
    visit(context->exp());
    GlobalContext &gctx = GlobalContext::GetInstance();
    if (context->opUnary()->getText() == "-"){
        setValue(context, gctx.builder->CreateNeg(getValue(context->exp())));
    }
    if (context->opUnary()->getText() == "+"){
        setValue(context, getValue(context->exp()));
    }
    
    #ifdef TEST
    std::cout << "end visit exp unary" << std::endl;
    #endif
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitExpParent(calculatorParser::ExpParentContext *context) {
    #ifdef TEST
    std::cout << "start visit exp parent" << std::endl;
    #endif
    visit(context->exp());
    // GlobalContext &gctx = GlobalContext::GetInstance();
    setValue(context, getValue(context->exp()));
    #ifdef TEST
    std::cout << "end visit exp parent" << std::endl;
    #endif
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitExpNumber(calculatorParser::ExpNumberContext *context) {
    #ifdef TEST
    std::cout << "start visit exp number" << std::endl;
    #endif
    visit(context->number());
    // GlobalContext &gctx = GlobalContext::GetInstance();
    setValue(context, getValue(context->number()));
    #ifdef TEST
    std::cout << "end visit exp number" << std::endl;
    #endif
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitExpPow(calculatorParser::ExpPowContext *context) {
    #ifdef TEST
    std::cout << "start visit exp pow" << std::endl;
    #endif
    // visit(context->exp(0));
    // visit(context->exp(1));
    // setValue(context, pow(getValue(context->exp(0)), getValue(context->exp(1))));
    #ifdef TEST
    std::cout << "end visit exp pow" << std::endl;
    #endif
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitExpMulDiv(calculatorParser::ExpMulDivContext *context) {
    #ifdef TEST
    std::cout << "start visit exp muldivmod" << std::endl;
    #endif
    visit(context->exp(0));
    visit(context->exp(1));
    GlobalContext &gctx = GlobalContext::GetInstance();
    if (context->opMulDiv()->getText() == "*"){
        setValue(context, gctx.builder->CreateFMul(getValue(context->exp(0)), getValue(context->exp(1))));
    }
    if (context->opMulDiv()->getText() == "/"){
        setValue(context, gctx.builder->CreateFDiv(getValue(context->exp(0)), getValue(context->exp(1))));
    }
    #ifdef TEST
    std::cout << "end visit exp muldivmod" << std::endl;
    #endif
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitExpAddSub(calculatorParser::ExpAddSubContext *context) {
    #ifdef TEST
    std::cout << "start visit exp addsub" << std::endl;
    #endif
    visit(context->exp(0));
    visit(context->exp(1));
    GlobalContext &gctx = GlobalContext::GetInstance();
    if (context->opAddSub()->getText() == "+"){
        setValue(context, gctx.builder->CreateFAdd(getValue(context->exp(0)), getValue(context->exp(1))));
    }
    if (context->opAddSub()->getText() == "-"){
        setValue(context, gctx.builder->CreateFSub(getValue(context->exp(0)), getValue(context->exp(1))));
    }
    #ifdef TEST
    std::cout << "end visit exp addsub" << std::endl;
    #endif
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitNumber(calculatorParser::NumberContext *context) {
    #ifdef TEST
    std::cout << "start visit number" << std::endl;
    #endif
    GlobalContext &gctx = GlobalContext::GetInstance();
    if (context->Int()!=nullptr) {
        setValue(context, llvm::ConstantFP::get(*(gctx.context),llvm::APFloat(std::stod(context->Int()->getText()))));
    }
    if (context->Float()!=nullptr) {
        setValue(context, llvm::ConstantFP::get(*(gctx.context),llvm::APFloat(std::stod(context->Float()->getText()))));
    }
    #ifdef TEST
    std::cout << "end visit number" << std::endl;
    #endif
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitOpUnary(calculatorParser::OpUnaryContext *context) {
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitOpMulDiv(calculatorParser::OpMulDivContext *context) {
    return antlrcpp::Any(nullptr);
}

antlrcpp::Any Visitor::visitOpAddSub(calculatorParser::OpAddSubContext *context) {
    return antlrcpp::Any(nullptr);
}